package com.school.sports.websocket;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.school.sports.entity.Message;
import com.school.sports.service.MessageService;
import com.school.sports.util.CurrentHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Map;

/**
 * 消息WebSocket服务器端点
 * 用于处理用户消息的实时推送
 *
 * @author 系统管理员
 * @version 1.0
 * @since 2023-10-12
 */
@Slf4j
@Component
@ServerEndpoint("/websocket/message/{userId}")
public class MessageWebSocketServer {

    // 静态变量，用来记录当前在线连接数
    private static final AtomicInteger onlineCount = new AtomicInteger(0);

    // concurrent包的线程安全Set，用来存放每个客户端对应的WebSocketServer对象
    private static final ConcurrentHashMap<String, MessageWebSocketServer> webSocketMap = new ConcurrentHashMap<>();

    // 与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;

    // 接收userId
    private String userId = "";

    // 静态注入MessageService
    private static MessageService messageService;

    @Autowired
    public void setMessageService(MessageService messageService) {
        MessageWebSocketServer.messageService = messageService;
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;

        // 检查是否已存在连接
        if (webSocketMap.containsKey(userId)) {
            // 如果已存在，先关闭旧连接
            MessageWebSocketServer oldServer = webSocketMap.get(userId);
            if (oldServer != null) {
                try {
                    oldServer.session.close();
                } catch (IOException e) {
                    log.error("关闭旧连接失败", e);
                }
            }
        }

        webSocketMap.put(userId, this);
        onlineCount.incrementAndGet();

        log.info("用户连接:{}, 当前在线人数:{}", userId, onlineCount.get());

        // 发送连接成功消息
        try {
            JSONObject message = new JSONObject();
            message.put("type", "connection");
            message.put("status", "connected");
            message.put("message", "连接成功");
            sendMessage(message.toJSONString());
        } catch (IOException e) {
            log.error("用户:{} 网络异常!", userId, e);
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        if (webSocketMap.containsKey(userId)) {
            webSocketMap.remove(userId);
            onlineCount.decrementAndGet();
        }
        log.info("用户退出:{}, 当前在线人数:{}", userId, onlineCount.get());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("用户消息:{}, 报文:{}", userId, message);

        try {
            JSONObject jsonObject = JSON.parseObject(message);
            String type = jsonObject.getString("type");

            switch (type) {
                case "heartbeat":
                    // 心跳检测
                    sendHeartbeatResponse();
                    break;
                case "mark_read":
                    // 标记消息已读
                    handleMarkRead(jsonObject);
                    break;
                case "get_unread_count":
                    // 获取未读消息数量
                    sendUnreadCount();
                    break;
                default:
                    log.warn("未知消息类型: {}", type);
            }
        } catch (Exception e) {
            log.error("处理消息异常", e);
        }
    }

    /**
     * 处理标记已读请求
     */
    private void handleMarkRead(JSONObject jsonObject) {
        try {
            Long messageId = jsonObject.getLong("messageId");
            if (messageId != null && messageService != null) {
                // 更新消息状态为已读
                messageService.markAsRead(messageId, Long.valueOf(userId));

                // 返回更新结果
                JSONObject response = new JSONObject();
                response.put("type", "message_read");
                response.put("messageId", messageId);
                response.put("status", "success");
                sendMessage(response.toJSONString());
            }
        } catch (Exception e) {
            log.error("标记消息已读失败", e);
        }
    }

    /**
     * 发送未读消息数量
     */
    private void sendUnreadCount() {
        try {
            if (messageService != null) {
                int unreadCount = messageService.getUnreadMessageCount(Long.parseLong(userId));

                JSONObject response = new JSONObject();
                response.put("type", "unread_count");
                response.put("count", unreadCount);
                sendMessage(response.toJSONString());
            }
        } catch (Exception e) {
            log.error("获取未读消息数量失败", e);
        }
    }

    /**
     * 发送心跳响应
     */
    private void sendHeartbeatResponse() {
        try {
            JSONObject response = new JSONObject();
            response.put("type", "heartbeat");
            response.put("timestamp", System.currentTimeMillis());
            sendMessage(response.toJSONString());
        } catch (Exception e) {
            log.error("发送心跳响应失败", e);
        }
    }

    /**
     * 发生错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误:{}, 原因:{}", this.userId, error.getMessage(), error);
    }

    /**
     * 实现服务器主动推送消息
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 发送自定义消息
     */
    public static void sendInfo(String message, @PathParam("userId") String userId) throws IOException {
        log.info("发送消息到:{}, 报文:{}", userId, message);
        if (userId != null && webSocketMap.containsKey(userId)) {
            webSocketMap.get(userId).sendMessage(message);
        } else {
            log.error("用户{}不在线", userId);
        }
    }

    /**
     * 群发自定义消息
     */
    public static void sendAllMessage(String message) {
        log.info("群发消息: {}", message);
        webSocketMap.forEach((userId, webSocketServer) -> {
            try {
                webSocketServer.sendMessage(message);
            } catch (IOException e) {
                log.error("群发消息失败，用户:{}", userId, e);
            }
        });
    }

    /**
     * 发送新消息通知
     */
    public static void sendNewMessage(Message message, String... userIds) {
        JSONObject notification = new JSONObject();
        notification.put("type", "new_message");

        JSONObject payload = new JSONObject();
        payload.put("id", message.getId());
        payload.put("title", message.getTitle());
        payload.put("content", message.getContent());
        payload.put("messageType", message.getType());
        payload.put("priority", message.getPriority());
        payload.put("createAt", message.getCreatedAt());
        payload.put("senderName", message.getSenderName());

        notification.put("payload", payload);

        String messageJson = notification.toJSONString();

        if (userIds != null && userIds.length > 0) {
            // 发送给指定用户
            for (String userId : userIds) {
                try {
                    sendInfo(messageJson, userId);
                } catch (IOException e) {
                    log.error("发送新消息通知失败，用户:{}", userId, e);
                }
            }
        } else {
            // 群发
            sendAllMessage(messageJson);
        }
    }

    /**
     * 获取当前在线连接数
     */
    public static int getOnlineCount() {
        return onlineCount.get();
    }

    /**
     * 获取当前在线用户列表
     */
    public static Map<String, MessageWebSocketServer> getOnlineUsers() {
        return webSocketMap;
    }

    /**
     * 检查用户是否在线
     */
    public static boolean isUserOnline(String userId) {
        return webSocketMap.containsKey(userId);
    }
}